#pragma once

#include <iostream>
#include <queue>
#include <pthread.h>
#include "mutex.hpp"
#include "cond.hpp"

namespace BlockQueue
{
    using namespace Lock;
    using namespace Condmodle;

    // 缺省参数
    static int gcap = 10;

    template <class T>
    class BQ
    {
    private:
        std::queue<T> _q;
        int _cap;
        // 注意一把锁就行，因为生产者与生产者之间，消费者与消费者之间是互斥关系：
        mutex _mutex;
        cond _product_cond;
        cond _consumer_cond;
        int pwaitnum;
        int cwaitnum;

    private:
        bool is_full() { return _q.size() == _cap; }
        bool is_empty() { return _q.empty(); }

    public:
        BQ(int cap = gcap)
            : _cap(cap), pwaitnum(0), cwaitnum(0)
        {
        }

        void Pop(T*out)
        {
            LockGuard lockguard(_mutex);
            while (is_empty())
            {
               cwaitnum++;
               _consumer_cond.Wait(_mutex);
               cwaitnum--;
            }

            *out = _q.front();
            _q.pop();
            if(pwaitnum>0)
            {
                _product_cond.Notify();
            }
        }
        void Equeue(const T &in)
        {
            LockGuard lockguard(_mutex);
            while (is_full()) // 使用while，防止伪唤醒：竞争条件等
            {
                pwaitnum++;
                _product_cond.Wait(_mutex);
                pwaitnum--;
            }

            _q.push(in);
            if (cwaitnum > 0)
            {
                _consumer_cond.Notify();
            }
        }
    };

}